/*
 * GraphicUserInterface.cpp
 *
 *  Created on: 27/09/2009
 *      Author: mario
 */

#include "GraphicUserInterface.h"


GraphicUserInterface::GraphicUserInterface() {

}

GraphicUserInterface::~GraphicUserInterface() {

}

int spacer()
{
  int i;

  for ( i = 0; i < SEPARATION; i++ )
    putchar ( '\n' );

  return 0;
}

void printHeader(string msg){
	cout << SPACER;
	cout << msg;
	cout << SPACER;
	putchar ( '\n' );

}

void printMainMenu(int openFiles){
	printHeader("                       MENU DE OPCIONES                      \n");

    cout << "   1- Carga De Archivos\n";
    spacer();
    if(openFiles){
    	cout << "   2- Camino por Direccion\n";
    	spacer();
    	cout << "   3- A Establecimiento\n";
		spacer();
		cout << "   4- Salir\n";
    }else{
    	cout << "   2- Salir\n";
    }

    putchar ( '\n' );
    cout << SPACER;
    cout << "Su eleccion: ";
}

void printFileMenu(){
	printHeader("                         Carga De Archivos\n");

    cout << "   1- Carga Archivos Del Grafo\n";

    spacer();

    cout << "   2- Salir\n";

    cout << SPACER;
	cout << "Su eleccion: ";
}

int loadHandler(ifstream* fileV, ifstream* fileE,ifstream* fileM){
	
	if(fileV->is_open()){
		fileV->close();
		fileV->clear();
	}
	if(fileE->is_open()){
		fileE->close();
		fileE->clear();
	}

	if(fileM->is_open()){
		fileM->close();
		fileM->clear();
	}

	string vertexFile;
	string edgeFile;
	string mapObjectFile;

	int exit = 0;
	string selection = "a";

	while(!exit){
		cout << '\n';
		cout << "Ingrese el directorio en el que se encuentra el archivo de vertices: ";
		getline(cin, vertexFile);

		fileV->open(vertexFile.c_str(), ifstream::out);

		if (!fileV->is_open()) {
			fileV->clear();
			while (selection != "n" && selection != "y"){
				cout << '\n';
				cout << "El archivo no existe no es valido,desea intentarlo nuevamente [y/n]: ";
				getline(cin, selection);
			}
			if(selection == "n"){
				return 0;
			}
		}else{
			exit=1;
		}

	}

	exit = 0;
	selection = '0';

	while(!exit){
		cout << '\n';
		cout << "Ingrese el directorio en el que se encuentra el archivo de aristas: ";
		getline(cin, edgeFile);
		fileE->open(edgeFile.c_str(), ifstream::out);

		if (!fileE->is_open()) {
			fileE->clear();
			while (selection != "n" && selection != "y"){
				cout << '\n';
				cout << "El archivo no existe no es valido,desea intentarlo nuevamente [y/n]: ";
				getline(cin, selection);
			}
			if(selection == "n"){
				return 0;
			}
		}else{
			exit=1;
		}

	}

	exit = 0;
	selection = '0';

	while(!exit){
		cout << '\n';
		cout << "Ingrese el directorio en el que se encuentra el archivo de objetos de mapa: ";
		getline(cin, mapObjectFile);
		fileM->open(mapObjectFile.c_str(), ifstream::out);

		if (!fileM->is_open()) {
			fileM->clear();
			while (selection != "n" && selection != "y"){
				cout << '\n';
				cout << "El archivo no existe no es valido,desea intentarlo nuevamente [y/n]: ";
				getline(cin, selection);
			}
			if(selection == "n"){
				return 0;
			}
		}else{
			exit=1;
		}
	}
	return 1;
}

void printNodeMenu(int option){
	if(option == 2){
		printHeader("                       OBTENCION CAMINO RAPIDO\n");
	}else{
		printHeader("                       OBTENCION CAMINO CORTO\n");
	}

}

void GraphicUserInterface::searchNodepath(int option){
	printNodeMenu(option);
	string oStreet;
	string dStreet;
	string oAdd;
	string dAdd;
	string edgeFrom;
	string edgeTo;
	string search;
	int from;
	int to;
	GraphManager* graphManager = new GraphManager(map->getGraph());
	do {
		cout << "Ingrese La calle de Origen: ";
		getline(cin, oStreet);
		do{
			cout << "\n";
			cout << "Ingrese el numero: ";
			getline(cin, oAdd);
		}while(!Utils::validatePositiveInt(oAdd));

		from = Utils::stringToInt(oAdd);
		edgeFrom = graphManager->validateAddres(oStreet,from);

		cout << "\n";
		cout << "Ingrese el lugar o la calle de Destino: ";
		getline(cin, dStreet);

		do{
			cout << "\n";
			cout << "Ingrese el numero: ";
			getline(cin, dAdd);
		}while(!Utils::validatePositiveInt(dAdd));

		to = Utils::stringToInt(dAdd);
		edgeTo = graphManager->validateAddres(dStreet,to);

		if ( edgeFrom == "" || edgeTo == "") {
			if (edgeFrom == "") {
				cout << "No se encontro la direccion de origen. Desea realizar nuevamente la busqueda [y/n]: ";
			} else {
				cout << "No se encontro la direccion de destino. Desea realizar nuevamente la busqueda [y/n]: ";
			}
		} else {
			
			List* path = NULL;
			
			if(edgeFrom.compare(edgeTo) == 0){
				Edge *edge = map->getGraph()->getEdge(edgeTo);
				if(!(edge->getLastAddress() > edge->getInitAddress() && (to - from) > 0)
					&& !(edge->getLastAddress() > edge->getInitAddress() && (to - from) > 0)){
					if (option == 2) {
						path = graphManager->getFastestPathFromEdges(edgeFrom, edgeTo);
					}
					if (option == 3) {
						path = graphManager->getShortestPathFromEdges(edgeFrom, edgeTo);
					}
				}
			}else{
				if (option == 2) {
					path = graphManager->getFastestPathFromEdges(edgeFrom, edgeTo);
				}
				if (option == 3) {
					path = graphManager->getShortestPathFromEdges(edgeFrom, edgeTo);
				}
			}

			printResults(path);
			MainThread* mainThread = new MainThread();
			mainThread->run(map,path,edgeFrom,from,edgeTo,to, null, option);
			delete mainThread;
			cout << "\n";
			cout << "Desea realizar otra busqueda [y/n]: ";
		}
		getline(cin, search);
		cout << '\n';
		
	} while ((search != "n") && (search != "N"));

}

void printPathMenu(){
	printHeader("                       Modo de Busqueda                      \n");

	cout << "   1- Obtener Camino Mas Rapido\n";
	spacer();
	cout << "   2- Obtener Camino Mas Corto\n";
	spacer();
	cout << "   3- Atras\n";

    putchar ( '\n' );
    cout << SPACER;
    cout << "Su eleccion: ";
}

void GraphicUserInterface::pathToAddres(){
	int option = -1;
	string op;
	while(option != 3){
		printPathMenu();

		getline(cin, op);

		option = Utils::stringToInt(op);
		switch(option){

		case 1:
			   searchNodepath(option +1);
			   break;
		
		case 2:
				searchNodepath(option +1);
				break;
		default:
			break;

		}
	}
}

void GraphicUserInterface::searchPathToObject(int option){
	printNodeMenu(option);
	string oStreet;
	string oAdd;
	string edgeFrom;
	string search;
	string building;
	int from;
	GraphManager* graphManager = new GraphManager(map->getGraph());
	do {
		cout << "Ingrese La calle de Origen: ";
		getline(cin, oStreet);
		do{
			cout << "\n";
			cout << "Ingrese el numero: ";
			getline(cin, oAdd);
		}while(!Utils::validatePositiveInt(oAdd));

		from = Utils::stringToInt(oAdd);
		edgeFrom = graphManager->validateAddres(oStreet,from);

		cout << "\n";
		cout << "Ingrese el lugar Destino: ";
		getline(cin, building);
		List* objects  = map->getMapObjectsByType(building);

		if (edgeFrom == "" || objects->getSize() == 0) {
			if (edgeFrom == "") {
				cout << "No se encontro la direccion de origen. Desea realizar nuevamente la busqueda [y/n]: ";
			}else{
				cout << "No se encontro ningun lugar. Desea realizar nuevamente la busqueda [y/n]: ";
			}
		} else {
			
			MapObject* near = new MapObject();
			List* path = NULL;
			
			if (option == 1) {
				path = graphManager->getFastestPathFromEdgeToBuilding(edgeFrom,from, objects,*near);
			}
			if (option == 2) {
				path = graphManager->getClosestPathFromEdgeToBuilding(edgeFrom,from, objects,*near);
			}
			
			printResults(path);
			MainThread* mainThread = new MainThread();
			mainThread->run(map,path,edgeFrom,from,near->getIdEdge(),near->getNumber(), objects, option);
			delete mainThread;
			cout << "\n";
			cout << "Desea realizar otra busqueda [y/n]: ";
		}
		getline(cin, search);
		cout << '\n';
		
	} while ((search != "n") && (search != "N"));

}

void GraphicUserInterface::pathToObj(){
	int option = -1;
	string op;
	while(option != 3){
		printPathMenu();

		getline(cin, op);

		option = Utils::stringToInt(op);
		switch(option){

		case 1:
			   searchPathToObject(option);
			   break;
		
		case 2:
				searchPathToObject(option);
				break;
		default:
			break;

		}
	}
}

void GraphicUserInterface::menu(){
	int option;
	string op;
	int logout = 0;
	int openedFiles = 0;
	ifstream fileV;
	ifstream fileE;
	ifstream fileM;
	InputManager* inputManager = NULL;

	while(logout != 1) {

		printMainMenu(openedFiles);

		getline(cin, op);

		option = Utils::stringToInt(op);

		switch(option){

		case 1:
			   if (loadHandler(&fileV, &fileE, &fileM) == 2) {
				   logout = 1;
			   }

			   if (fileE.is_open() && fileV.is_open() && fileM.is_open()) {
				   openedFiles = 1;
			   } else {
				   openedFiles = 0;
			   }

			   inputManager = new InputManager(&fileV, &fileE, &fileM);

			   map = inputManager->createMap();

			   break;
		// fastest path
		case 2:
				if(!openedFiles){
					logout = 1;
					break;
				}

				pathToAddres();

				break;
		// shortest path
		case 3:
				if(!openedFiles){
					logout = 1;
					break;
				}

				pathToObj();

				break;

		case 4:
			logout = 1;
			break;

		default:
			break;

		}
	}
	if(inputManager != NULL){
		delete inputManager;
	}
}

void GraphicUserInterface::printResults(List* vertexListPath) {
	cout << "\n";
	if(vertexListPath == null){
		return;
	}
	if (vertexListPath->getSize() == 0) {
		//cout << "No existe conexion entre el vertice de origen y el de destino";
		return;
	}
	cout << "|\tID\t|\tDist. al Nodo\t|\tDist. Parcial\t|\tTiempo al Nodo\t|\tTiempo Parcial\t|\n\n";
	float partialDistance = 0.0;
	float totalDistance = 0.0;
	float partialTime = 0.0;
	float totalTime = 0.0;
	float minWeight = 0.0;

	for (int i = vertexListPath->getSize() - 1; i >= 0; i-- ) {
		cout << "|\t";
		if(i == vertexListPath->getSize() - 1){
			cout << ((VertexNode*)vertexListPath->get(i))->getNodeValue()->getId() << "\t|\t";
			cout << "0\t\t|\t";
			cout << "0\t\t|\t";
			cout << "0\t\t|\t";
			cout << "0\t\t|\n";
		}else{
			cout << ((VertexNode*)vertexListPath->get(i))->getNodeValue()->getId() << "\t|\t";
			VertexNode* temp = ((VertexNode*)vertexListPath->get(i+1));
			string currentId = ((VertexNode*)vertexListPath->get(i))->getNodeValue()->getId();
			
			for(int j = 0; j < temp->getNodeValue()->getEdgesList()->getSize(); j++){
				EdgeNode * edge = ((EdgeNode*)temp->getNodeValue()->getEdgesList()->get(j));
				if(edge->getNodeValue()->getIdTo() == currentId){
					if(minWeight == 0.0){
						minWeight = map->getGraph()->getEdgeWeight(edge->getNodeValue());
						partialDistance = edge->getNodeValue()->getLength();
						partialTime = edge->getNodeValue()->getTime();
					}else{
						float newWeight = map->getGraph()->getEdgeWeight(edge->getNodeValue());
						if(newWeight < minWeight){
							minWeight = newWeight;
							partialDistance = edge->getNodeValue()->getLength();
							partialTime = edge->getNodeValue()->getTime();
						}
					}
				}
			}
			minWeight = 0.0;
			totalDistance = partialDistance + totalDistance;
			std:: stringstream str; 
			str << partialDistance;
			cout << str.str() << " m\t\t|\t";
			partialDistance = 0.0;

			std:: stringstream distance;
			distance << totalDistance;
			cout << distance.str() << " m\t\t|\t";

			totalTime = partialTime + totalTime;
			std:: stringstream pTime; 
			pTime << partialTime;
			cout << pTime.str() << " min\t|\t";
			partialTime = 0.0;

			std:: stringstream tTime;
			tTime << totalTime;
			cout << tTime.str() << " min\t|\n";
		}
	}
}
